#include "zen_vap_activity.h"
#include "zen_vap_action.h"
#include "zen_vap_layer.h"
#include "zen_app.h"
#include "zen_ticker.h"
#include "zen_log.h"
#include <vector>
#include <mutex>
#include <queue>
#include <map>

namespace Zen { namespace Vap {

	struct ActionItem
	{
		std::shared_ptr<Action> action;
		std::weak_ptr<Node> node;
		double next_time;
		double time;
		bool valid = true;
	};

	class Activity_i : public Activity {
	protected:

		std::deque<std::shared_ptr<ActionItem> > m_actions_running;
		std::vector<std::shared_ptr<ActionItem> > m_actions_new;

		std::map<uint32_t/*AppTouch::no*/, TouchInfo > m_touches;

		Size2 m_real_size;

		std::shared_ptr<Node> m_root_node;
		std::shared_ptr<RootDelegate> m_delegate;
		Color4f m_color;

		double m_running_duration; // running duration
		std::chrono::microseconds m_start_time;

	public:
		static std::shared_ptr<Activity_i> Get();

		Activity_i();

		~Activity_i();

		void pushActionRunning(std::shared_ptr<ActionItem> item);

		void runAction_(Node* node, std::shared_ptr<Action> action);

		void stopNodeActions_(Node* node);

		void stopAction_(Action * action);

		void updateActions_();

		void clean();

	public: // override
		virtual void setDelegate(std::shared_ptr<RootDelegate> delegate) override;
		virtual std::shared_ptr<RootDelegate> getDelegate() override;
		virtual Size2 getRealViewSize() override;
		virtual void setBackgroundColor(Color4f const & color) override;
		virtual Color4f getBackgroundColor() override;
		virtual double getRunningTime() override;
//		virtual void replaceRootNode(std::shared_ptr<Node> node) override;
		virtual std::shared_ptr<Node> getRootNode() override;

		virtual double getTotalTime() override;

	public:
		virtual void onLaunch(Zen::Size2 view_size) override;

		virtual void onResize(Zen::Size2 view_size) override;

		virtual void onExit() override;

		virtual void onPause() override;

		virtual void onResume() override;

		virtual void onUpdate() override;

		virtual void onDraw() override;

		virtual void onTouchDown(AppTouch touch) override;

		virtual void onTouchMove(AppTouch touch) override;

		virtual void onTouchUp(AppTouch touch) override;

		virtual void onTouchCancel(AppTouch touch) override;

		virtual std::map<uint32_t, TouchInfo> const & getTouches() override {
			return m_touches;
		}

	};

}}


namespace Zen { namespace Vap {

	void Activity_i::onLaunch(Zen::Size2 view_size)
	{
		Zen::LogV("%s", __FUNCTION__);

		this->setBackgroundColor(Color4f(0x333355ff));

		Helper::Get()->initRender();

		m_real_size = view_size;
		
		m_running_duration = 0.0; //std::chrono::microseconds::zero();
		
		if(m_delegate) m_delegate->onLaunch(m_real_size);
	}

	void Activity_i::onResize(Zen::Size2 view_size)
	{
		Zen::LogV("%s", __FUNCTION__);

		m_real_size = view_size;
		if(m_delegate) m_delegate->onResize(m_real_size);
	}

	void Activity_i::onExit()
	{
		Zen::LogV("%s", __FUNCTION__);

		if(m_delegate) m_delegate->onExit();
	}

	void Activity_i::onPause()
	{
		Zen::LogV("%s", __FUNCTION__);
		if(m_delegate) m_delegate->onPause();
	}

	void Activity_i::onResume()
	{
		Zen::LogV("%s", __FUNCTION__);

		if(m_delegate) m_delegate->onResume();
		
		std::cout << m_touches.size() << std::endl;
	}

	void Activity_i::onUpdate()
	{
		this->clean();

		auto interval = 1.f / App::Get()->getFramesPerSecond(); // m_ticker.tick();

		m_running_duration += interval;

		this->updateActions_();

		if(m_delegate) m_delegate->onUpdate();
		
	}

	void Activity_i::onDraw()
	{
		this->clean();

		if(m_delegate) m_delegate->onDraw();

		{
			RenderGuard r;
			m_root_node->draw();
		}
		
		this->clean();
	}

	void Activity_i::onTouchDown(AppTouch touch)
	{
		this->clean();

		if(m_delegate) m_delegate->onTouchDown(touch);

		auto & info = m_touches[touch.no];
		auto node = m_root_node->touchDown(touch);
		info.attached_node = node? node->shared_from_this() : std::weak_ptr<Node>();
		info.touch = touch;
	}

	void Activity_i::onTouchMove(AppTouch touch)
	{
		this->clean();

		if(m_delegate) m_delegate->onTouchMove(touch);

		auto iter = m_touches.find(touch.no);

		musts(iter != m_touches.end(), "touch moves without down");

		auto attached = iter->second.attached_node.lock();
		if(attached) {
			auto node = attached->touchMove(touch);
			if(node == nullptr)
			{
				iter->second.attached_node.reset();
			}
			else if(attached.get() != node)
			{
				iter->second.attached_node = node->shared_from_this();
			}
		}
		iter->second.touch = touch;
	}

	void Activity_i::onTouchUp(AppTouch touch)
	{
		this->clean();

		if(m_delegate) m_delegate->onTouchUp(touch);

		auto iter = m_touches.find(touch.no);
		musts(iter != m_touches.end(), "touch up without down");

		auto attached = iter->second.attached_node.lock();
		if(attached) attached->touchUp(touch);

		m_touches.erase(iter);

		this->clean();
	}

	void Activity_i::onTouchCancel(AppTouch touch)
	{
		if(m_delegate) m_delegate->onTouchCancel(touch);

		auto iter = m_touches.find(touch.no);
		musts(iter != m_touches.end(), "touch cancel without down");

		auto attached = iter->second.attached_node.lock();
		if(attached) attached->touchCancel(touch);

		m_touches.erase(iter);
		
		this->clean();
	}
}}

namespace Zen { namespace Vap {

	std::shared_ptr<Activity> Activity::Get()
	{
		return Activity_i::Get();
	}
	std::shared_ptr<Activity_i> Activity_i::Get()
	{
		static auto root = std::make_shared<Activity_i>();
		return root;
	}
	Activity_i::Activity_i()
	{
		m_root_node = std::make_shared<Layer>();
	}

	Activity_i::~Activity_i()
	{
		m_root_node.reset();
	}

	void Activity_i::setDelegate(std::shared_ptr<RootDelegate> delegate)
	{
		m_delegate = delegate;
	}

	void Activity_i::setBackgroundColor(Color4f const & color)
	{
		m_color = color;
		Helper::Get()->setClearColor(color);
	}

	Color4f Activity_i::getBackgroundColor()
	{
		return m_color;
	}
	
	Size2 Activity_i::getRealViewSize()
	{
		return m_real_size;
	}

	std::shared_ptr<Node> Activity_i::getRootNode()
	{
		return m_root_node;
	}
	double Activity_i::getRunningTime()
	{
		return m_running_duration;
	}
	double Activity_i::getTotalTime()
	{
		return Zen::ToSeconds(Now() - m_start_time);
	}
	std::shared_ptr<RootDelegate> Activity_i::getDelegate()
	{
		return m_delegate;
	}

	void Activity_i::pushActionRunning(std::shared_ptr<ActionItem> item)
	{
		for(auto iter = m_actions_running.begin(); iter != m_actions_running.end(); ++iter)
		{
			if((*iter)->next_time >= item->next_time)
			{
				m_actions_running.insert(iter, item);
				return;
			}
		}
		m_actions_running.insert(m_actions_running.end(), item);
	}

	void Activity_i::runAction_(Node* node, std::shared_ptr<Action> action)
	{
		if(!node || !action ) return;
		auto i = std::make_shared<ActionItem>();
		i->node = node->shared_from_this();
		i->action = action;
		m_actions_new.push_back(i);
	}

	void Activity_i::stopNodeActions_(Node * node)
	{
		for(auto i : m_actions_running)
		{
			if(i->node.lock().get() == node) i->valid = false;
		}
		for(auto i : m_actions_new)
		{
			if(i->node.lock().get() == node) i->valid = false;
		}
	}

	void Activity_i::stopAction_(Action * action)
	{
		for(auto i : m_actions_running)
		{
			if(i->action.get() == action)
			{
				i->valid = false;
				break;
			}
		}
		for(auto & i : m_actions_new)
		{
			if(i->action.get() == action)
			{
				i->valid = false;
				break;
			}
		}
	}

	void Activity_i::updateActions_()
	{
		while(m_actions_running.size())
		{
			auto i = m_actions_running.front();

			if(!i->valid) {
				m_actions_running.pop_front();
				continue;  /// deleted i;
			}

			if(i->next_time > m_running_duration)
				break; /// not the time.

			auto interval = m_running_duration - i->time;

			auto next = i->action->run((float)interval);

			/// !!! when `action->run` must keep the action in queue,  so pop after `action->run`
			m_actions_running.pop_front();

			/**
			 here check valid again, when action->run() the stopAction()/node->remove() maybe be called.
			 */
			if(i->valid && next >= 0) // running
			{
				i->time = m_running_duration;
				// interval should larger than 0, otherwise a dead circle happended.
				i->next_time = m_running_duration + std::max((double)next, 1/120.0);

				pushActionRunning(i);
			}
			/// else delete i;
		}

		for(auto i : m_actions_new)
		{
			if(! i->valid || i->node.expired()) continue;

			auto delay = i->action->start();

			if(delay >= 0 && i->valid)
			{
				i->time = m_running_duration;

				i->next_time = m_running_duration + delay; // here delay can be 0.

				pushActionRunning(i);
			}
		}
		m_actions_new.clear();
	}

	void Activity_i::clean()
	{
	}
}}

/**
 @ref part of node
 */

namespace Zen { namespace Vap {


	void Node::runAction(std::shared_ptr<Action> action)
	{
		Activity_i::Get()->runAction_(this, action);
	}
	void Node::stopAction(Action * action)
	{
		if(action) Activity_i::Get()->stopAction_(action);
	}
	void Node::stopAction(std::shared_ptr<Action> action)
	{
		Activity_i::Get()->stopAction_(action.get());
	}
	void Node::stopAllActions()
	{
		Activity_i::Get()->stopNodeActions_(this);
	}
}}
